7 Redes Neurais
Nos capítulos anteriores, estudamos modelos lineares e transformações de atributos. Redes neurais combinam essas duas ideias de modo adaptativo: em vez de escolher manualmente todos os atributos, o treinamento ajusta sucessivas transformações a partir dos dados.
Em aprendizado supervisionado, recebemos uma amostra
\[ D=\{(x_i,y_i)\}_{i=1}^N, \qquad x_i\in X,\quad y_i\in Y, \]
e escolhemos uma hipótese parametrizada \(h_\theta:X\to\widehat Y\). O vetor \(\theta\) reúne todos os pesos e vieses da rede. Uma função de perda
\[ \ell:\widehat Y\times Y\longrightarrow[0,\infty) \]
mede a qualidade de uma predição individual. O erro empírico médio é
\[ E_D(\theta) =\frac1N\sum_{i=1}^N\ell(h_\theta(x_i),y_i). \]
O algoritmo de treinamento procura parâmetros que reduzam esse erro, normalmente com regularização e um método baseado em gradientes. O objetivo real continua sendo generalizar para exemplos não observados; memorizar a amostra não é suficiente.
7.1 Da unidade artificial à rede
Um neurônio artificial recebe um vetor \(x\in\mathbb R^d\), calcula uma combinação afim e aplica uma função de ativação \(g\):
\[ z=w^{\mathsf T}x+b, \qquad a=g(z). \tag{7.1}\]
Aqui, \(w\in\mathbb R^d\) contém os pesos, \(b\in\mathbb R\) é o viés, \(z\) é a pré-ativação e \(a\) é a ativação produzida. O viés permite deslocar o limiar; sem ele, muitas fronteiras seriam forçadas a passar pela origem.
Uma camada com \(m\) neurônios calcula todas essas operações em paralelo:
\[ z=Wx+b, \qquad a=g(z), \]
onde \(W\in\mathbb R^{m\times d}\), \(b\in\mathbb R^m\) e \(g\) é aplicada componente a componente. Cada linha de \(W\) corresponde aos pesos de um neurônio.
Uma rede feedforward, ou sem realimentação, encadeia camadas sem ciclos. Para duas camadas parametrizadas,
\[ a^{(1)}=g_1(W^{(1)}x+b^{(1)}), \]
\[ h_\theta(x)=g_2(W^{(2)}a^{(1)}+b^{(2)}). \]

Chamamos de camadas ocultas as transformações entre a entrada e a saída. A entrada em si não possui parâmetros e, por isso, não costuma ser contada como camada treinável. A expressão “rede de uma camada” pode ser ambígua: alguns autores contam apenas camadas ocultas, enquanto outros contam todas as camadas com pesos. Neste capítulo, diremos explicitamente “uma camada oculta” sempre que essa distinção importar.
7.2 Por que a ativação precisa ser não linear?
Se todas as ativações forem a identidade, duas camadas afins se reduzem a uma única transformação afim:
\[ W^{(2)}(W^{(1)}x+b^{(1)})+b^{(2)} =\underbrace{W^{(2)}W^{(1)}}_{W'}x +\underbrace{W^{(2)}b^{(1)}+b^{(2)}}_{b'}. \]
Empilhar transformações lineares, portanto, não aumenta a classe de funções representáveis. Ativações não lineares entre as camadas impedem essa redução e permitem construir fronteiras e aproximações complexas. A camada de saída, contudo, deve ser escolhida de acordo com a tarefa: identidade para regressão irrestrita, sigmoide para uma probabilidade binária e softmax para probabilidades entre várias classes são escolhas comuns.
7.3 Arquitetura e aprendizado
A arquitetura define o grafo de operações: quantidade de camadas, largura de cada camada, ativações e conexões. Os pesos e vieses são parâmetros aprendidos. Já largura, profundidade, taxa de aprendizado e força de regularização são hiperparâmetros escolhidos por validação.
O treinamento de uma rede feedforward pode ser resumido em quatro etapas repetidas:
- propagação para frente: calcular as ativações e a predição;
- perda: comparar a predição ao alvo;
- retropropagação: calcular derivadas pela regra da cadeia;
- atualização: modificar os parâmetros com um otimizador.
# Pseudocódigo de uma camada densa
z = W @ x + b
a = ativacao(z)Embora a expressão seja curta, é essencial acompanhar os formatos. Se x tem \(d\) componentes e a camada possui \(m\) neurônios, W tem formato (m, d), enquanto b, z e a têm \(m\) componentes. Erros de dimensão estão entre os problemas mais comuns nas primeiras implementações.
7.3.0.1 Exercícios
- Determine os formatos de \(W\) e \(b\) para uma camada que recebe 8 atributos e produz 32 ativações.
- Quantos parâmetros treináveis essa camada possui?
- Demonstre que a composição de três transformações afins ainda é afim.
- Diferencie parâmetro, hiperparâmetro, pré-ativação e ativação.
- Explique por que uma perda baixa no treino não garante boa generalização.
7.4 Funções de Ativação
Uma função de ativação transforma a pré-ativação \(z\) produzida por um neurônio. Nas camadas ocultas, sua função principal é introduzir não linearidade. Na saída, ela também determina o domínio da predição e deve ser compatível com a tarefa e com a perda.
Durante a retropropagação, a derivada da ativação participa da regra da cadeia. Assim, saturação, descontinuidade da derivada e escala dos gradientes afetam diretamente a otimização.

7.4.1 Função de Ativação ReLU (Unidade Linear Retificada)
A unidade linear retificada é definida por
\[ \operatorname{ReLU}(z)=\max(0,z). \]
Sua derivada, exceto em \(z=0\), é
\[ \operatorname{ReLU}'(z)= \begin{cases} 0, & z<0,\\ 1, & z>0. \end{cases} \]
No ponto zero a função não é diferenciável; bibliotecas adotam uma subderivada convencional, normalmente zero. A ReLU é barata e não satura no semieixo positivo, por isso é uma escolha frequente em camadas ocultas.
No semieixo negativo, porém, o gradiente é zero. Um neurônio que recebe pré-ativações negativas para todos os exemplos pode deixar de atualizar seus pesos: é o problema da ReLU morta. Inicialização, taxa de aprendizado e variantes com inclinação negativa ajudam a reduzi-lo.
7.4.2 Função de Ativação Leaky ReLU
A Leaky ReLU mantém uma pequena inclinação no lado negativo:
\[ \operatorname{LeakyReLU}_\alpha(z)= \begin{cases} z, & z>0,\\ \alpha z, & z\leq0, \end{cases} \qquad 0<\alpha<1. \]
Sua derivada vale \(1\) no lado positivo e \(\alpha\) no lado negativo, exceto pela convenção escolhida em zero. Dessa forma, um neurônio recebe algum gradiente mesmo quando sua pré-ativação é negativa. Valores como \(\alpha=0{,}01\) são comuns, mas \(\alpha\) é um hiperparâmetro e não uma constante universal.
7.4.3 Função de Ativação Sigmoide
A sigmoide logística é
\[ \sigma(z)=\frac1{1+e^{-z}}, \qquad 0<\sigma(z)<1. \]
Sua derivada pode ser calculada a partir da própria saída:
\[ \sigma'(z)=\sigma(z)(1-\sigma(z)). \]
O valor máximo da derivada é \(1/4\), atingido em \(z=0\). Para \(|z|\) grande, a saída se aproxima de zero ou um e a derivada se aproxima de zero. Em redes profundas, multiplicar sucessivamente derivadas pequenas contribui para o desaparecimento do gradiente.
A sigmoide é especialmente adequada à camada de saída de uma classificação binária, onde pode representar \(P(Y=1\mid x)\). Em camadas ocultas modernas, ReLU e variantes costumam ser preferidas.
Uma implementação direta de \(e^{-z}\) pode transbordar para \(z\) muito negativo. Funções numericamente estáveis tratam separadamente os casos \(z\geq0\) e \(z<0\).
7.4.4 Função de Ativação Tangente Hiperbólica (tanh)
A tangente hiperbólica é
\[ \tanh(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}, \qquad -1<\tanh(z)<1, \]
e sua derivada é
\[ \frac{d}{dz}\tanh(z)=1-\tanh^2(z). \]
Ao contrário da sigmoide, a tanh é centrada em zero. Isso pode facilitar a otimização quando ativações positivas e negativas são úteis. Ela também satura para entradas de grande magnitude e, portanto, não elimina o desaparecimento do gradiente. É frequente em formulações clássicas de redes recorrentes.
7.4.5 Softmax
Para classificação entre \(K\) classes mutuamente exclusivas, a camada de saída produz logits \(z\in\mathbb R^K\). A softmax os converte em probabilidades:
\[ \operatorname{softmax}(z)_i =\frac{e^{z_i}}{\sum_{j=1}^K e^{z_j}}, \qquad i=1,\ldots,K. \]
Cada componente é positiva e a soma é um. Somar a mesma constante a todos os logits não altera o resultado. Usamos essa propriedade para evitar transbordamento:
\[ \operatorname{softmax}(z)_i =\frac{e^{z_i-m}}{\sum_j e^{z_j-m}}, \qquad m=\max_jz_j. \]
A softmax acopla as saídas: aumentar a probabilidade de uma classe reduz a massa disponível para as outras. Por isso, ela se aplica a classes exclusivas. Em classificação multirrótulo, na qual várias classes podem ser verdadeiras simultaneamente, usamos uma sigmoide independente por rótulo.
7.4.6 Ativação de saída e tarefa
| Tarefa | Saída típica | Ativação | Perda comum |
|---|---|---|---|
| Regressão real | 1 ou mais valores | identidade | erro quadrático |
| Classificação binária | 1 logit | sigmoide | entropia cruzada binária |
| Multiclasse exclusiva | \(K\) logits | softmax | entropia cruzada categórica |
| Multirrótulo | \(K\) logits | \(K\) sigmoides | entropia cruzada binária |
Na prática, bibliotecas frequentemente combinam logits e entropia cruzada em uma única operação estável. Nesse caso, a função de perda espera logits crus e aplica internamente sigmoide ou softmax; aplicar a ativação antes pode duplicar a operação e prejudicar a estabilidade.
import numpy as np
def softmax_estavel(z):
z = np.asarray(z, dtype=float)
deslocado = z - np.max(z, axis=-1, keepdims=True)
exp_z = np.exp(deslocado)
return exp_z / np.sum(exp_z, axis=-1, keepdims=True)7.4.6.1 Exercícios
- Calcule ReLU, sigmoide e tanh para \(z\in\{-2,0,2\}\).
- Demonstre que \(\sigma'(z)=\sigma(z)(1-\sigma(z))\).
- Prove que a softmax é invariante à soma de uma constante em todos os logits.
- Explique por que softmax não é apropriada para uma imagem que pode conter simultaneamente “carro” e “pedestre”.
- O que acontece com os gradientes de uma pilha de sigmoides quando as pré-ativações têm grande magnitude?
7.5 Rede neural de camada única
Antes de estudar redes largas, é útil separar duas arquiteturas que às vezes recebem nomes semelhantes.
- Um único neurônio calcula \(g(w^{\mathsf T}x+b)\).
- Uma rede com uma camada oculta usa vários neurônios intermediários e depois combina suas ativações em uma saída.
Neste capítulo, “rede de camada única” significará uma rede com uma camada oculta, pois essa é a convenção usada nos teoremas de aproximação da seção seguinte.
7.5.1 Um único neurônio
Um neurônio com entrada \(x\in\mathbb R^d\) produz
\[ h(x)=g(w^{\mathsf T}x+b). \]
Escolhas diferentes de ativação e perda recuperam modelos já conhecidos:
| Modelo | Ativação de saída | Predição | Perda típica |
|---|---|---|---|
| Regressão linear | identidade | valor real | erro quadrático |
| Perceptron | sinal | classe em \(\{-1,+1\}\) | erro de classificação |
| Regressão logística | sigmoide | probabilidade binária | entropia cruzada |
| Regressão softmax | softmax sobre \(K\) logits | probabilidades de \(K\) classes | entropia cruzada categórica |
Todos esses modelos produzem fronteiras de decisão lineares no espaço de entrada. A ativação altera a saída e a perda apropriada, mas o nível \(w^{\mathsf T}x+b=0\) continua sendo um hiperplano. Um único neurônio não resolve, por exemplo, o XOR sem uma transformação prévia de atributos.
7.5.2 Uma camada oculta
Considere \(m\) neurônios ocultos, uma entrada com \(d\) atributos e uma saída escalar. A camada oculta calcula
\[ a_j(x)=g(w_j^{\mathsf T}x+b_j), \qquad j=1,\ldots,m. \]
A saída combina essas novas características:
\[ h_\theta(x) =g_{\mathrm{out}}\!\left( c+\sum_{j=1}^m v_j a_j(x) \right). \tag{7.2}\]
Em notação matricial,
\[ h_\theta(x) =g_{\mathrm{out}}\!\left( v^{\mathsf T}g(Wx+b)+c \right), \]
com
\[ W\in\mathbb R^{m\times d},\quad b\in\mathbb R^m,\quad v\in\mathbb R^m,\quad c\in\mathbb R. \]
Cada neurônio oculto aprende uma característica \(a_j(x)\). A camada de saída aprende como combiná-las. Diferentemente de uma transformação polinomial fixa, tanto as direções \(w_j\) quanto os deslocamentos \(b_j\) são ajustados pelos dados.
7.5.3 Contagem de parâmetros
A matriz \(W\) contém \(md\) pesos, o vetor \(b\) contém \(m\) vieses, a saída contém \(m\) pesos e um viés. Logo, a rede escalar em Equation 7.2 possui
\[ md+m+m+1=m(d+2)+1 \]
parâmetros treináveis. Para \(k\) saídas, usamos \(V\in\mathbb R^{k\times m}\) e \(c\in\mathbb R^k\), totalizando
\[ md+m+km+k \]
parâmetros. Essa contagem ajuda a estimar memória, comparar arquiteturas e identificar erros de implementação.
7.5.4 Propagação para frente em lote
Adote a convenção de que cada linha da matriz \(X\in\mathbb R^{N\times d}\) é um exemplo. Então
\[ Z^{(1)}=XW^{\mathsf T}+\mathbf1b^{\mathsf T} \in\mathbb R^{N\times m}, \]
\[ A^{(1)}=g(Z^{(1)}), \]
\[ Z^{(2)}=A^{(1)}V^{\mathsf T}+\mathbf1c^{\mathsf T} \in\mathbb R^{N\times k}. \]
O vetor de uns representa a transmissão do mesmo viés para todos os exemplos; bibliotecas fazem essa operação por broadcasting.
import numpy as np
def forward_uma_camada(X, W, b, V, c):
Z1 = X @ W.T + b
A1 = np.maximum(Z1, 0.0)
Z2 = A1 @ V.T + c
return Z1, A1, Z2Retornar valores intermediários é útil no aprendizado: a retropropagação reutiliza \(Z^{(1)}\) e \(A^{(1)}\) para calcular as derivadas.
7.5.5 Treinamento
Os parâmetros não são ajustados neurônio por neurônio de forma independente. A perda na saída gera gradientes que atravessam a camada de saída e chegam a todos os neurônios ocultos. Como diferentes permutações dos neurônios podem representar a mesma função e a função de perda não é, em geral, convexa nos parâmetros de todas as camadas, a solução não possui a simplicidade da regressão linear por mínimos quadrados.
Mesmo assim, derivadas eficientes permitem treinar redes grandes por descida do gradiente e suas variantes. Inicialização, escala dos dados, taxa de aprendizado, tamanho do lote e regularização influenciam o resultado.
7.5.5.1 Exercícios
- Conte os parâmetros de uma rede com \(d=10\), \(m=20\) e \(k=3\).
- Verifique os formatos de todas as matrizes no cálculo em lote.
- Explique por que trocar a ordem de dois neurônios ocultos e trocar as colunas correspondentes de \(V\) não altera a função da rede.
- Por que aplicar a função sinal dentro da camada oculta dificulta o treinamento por gradientes?
- Compare os atributos polinomiais fixos com as ativações aprendidas de uma camada oculta.
7.6 Teorema de aproximação universal para redes de camada única
Considere as funções produzidas por redes com uma camada oculta e saída linear:
\[ g(x)=c+\sum_{j=1}^{m}v_j\,sigma(w_j^{\mathsf T}x+b_j). \tag{7.3}\]
Cada valor de \(m\) define uma classe com largura finita. O teorema estuda a união dessas classes quando permitimos que \(m\) seja tão grande quanto necessário.
7.6.1 Enunciado
Seja \(K\subset\mathbb R^d\) compacto e seja \(\sigma:\mathbb R\to\mathbb R\) uma função contínua. As combinações da forma Equation 7.3 são densas em \(C(K)\), com a norma do supremo, se e somente se \(\sigma\) não é um polinômio.
Em termos concretos, para toda função contínua \(f:K\to\mathbb R\) e todo \(\varepsilon>0\), existe uma largura finita \(m\) e existem parâmetros \(w_j,b_j,v_j,c\) tais que
\[ \lVert f-g\rVert_\infty =\sup_{x\in K}|f(x)-g(x)|<\varepsilon. \tag{7.4}\]
Para uma saída vetorial \(f:K\to\mathbb R^k\), podemos aproximar cada componente e reuni-las em uma camada de saída com \(k\) unidades. Escolhendo o erro de cada componente suficientemente pequeno, obtemos uma aproximação na norma vetorial desejada.
7.6.2 Como interpretar o teorema
A palavra densa significa que podemos chegar arbitrariamente perto de qualquer elemento de \(C(K)\), não que uma única rede fixa represente todas as funções. A largura e os parâmetros dependem de \(f\), de \(K\) e da tolerância \(\varepsilon\).
A hipótese de compacidade também importa. Em \(\mathbb R^d\), um conjunto compacto é fechado e limitado. A norma do supremo em Equation 7.4 exige que a aproximação seja boa simultaneamente em todos os pontos desse domínio, uma garantia mais forte do que acertar somente uma amostra finita.
Ativações usuais como ReLU, sigmoide e tanh são não polinomiais e atendem à condição de expressividade. Se \(\sigma\) for um polinômio de grau \(q\), cada termo \(\sigma(w^{\mathsf T}x+b)\) tem grau no máximo \(q\); somas finitas continuam com grau no máximo \(q\). Essa classe de dimensão finita não pode aproximar uniformemente todas as funções contínuas.
7.6.3 Intuição com ReLU em uma dimensão
Uma combinação de ReLUs em uma variável,
\[ g(x)=a+sx+\sum_{j=1}^{m}v_j\operatorname{ReLU}(x-t_j), \]
é linear por partes. Antes de \(t_j\), o termo correspondente vale zero; depois de \(t_j\), ele altera a inclinação em \(v_j\). Portanto, colocando pontos de quebra \(t_j\) e escolhendo as mudanças de inclinação, podemos construir uma interpolação linear por partes.
Toda função contínua em um intervalo compacto é uniformemente contínua. Ao refinar suficientemente a malha, sua interpolação linear por partes fica uniformemente próxima da função. Isso fornece uma intuição concreta para a universalidade da ReLU em uma dimensão. Em dimensões maiores, a prova geral requer argumentos adicionais.
7.6.4 O que o teorema não garante
O resultado é de existência. Ele não afirma:
- qual largura \(m\) é necessária para uma função específica;
- que a rede será pequena ou computacionalmente eficiente;
- que descida do gradiente encontrará os parâmetros existentes;
- que uma amostra finita permitirá identificar a função;
- que a rede generalizará bem fora de \(K\);
- que uma camada oculta é sempre a melhor arquitetura.
Uma rede larga pode representar a solução e ainda ser difícil de treinar. Além disso, aproximar qualquer função contínua não implica aproximar com poucos dados. Expressividade, otimização e generalização são questões distintas.
7.6.5 Largura versus profundidade
O teorema permite concentrar toda a complexidade em uma camada oculta, mas não diz que isso seja eficiente. Algumas funções possuem descrição compacta como composição de operações simples. Redes profundas refletem essa estrutura composicional e podem representar certas funções com muito menos unidades que uma rede rasa.
Assim, “uma camada é universal” não torna a profundidade inútil. A universalidade responde se uma aproximação existe; estudos de eficiência investigam quantas unidades e camadas são necessárias.
7.6.6 Exemplo computacional: função linear por partes
import numpy as np
def relu(x):
return np.maximum(x, 0.0)
def rede_relu_1d(x, intercepto, inclinacao, pontos, saltos):
"""Representa uma função linear por partes usando ReLUs."""
x = np.asarray(x, dtype=float)
y = intercepto + inclinacao * x
for t, delta in zip(pontos, saltos):
y = y + delta * relu(x - t)
return yO vetor saltos não armazena as inclinações de cada trecho, mas a mudança de inclinação em cada ponto de quebra. Essa parametrização é uma representação explícita de uma rede ReLU com uma entrada e uma camada oculta.
7.6.6.1 Exercícios
- Explique a diferença entre erro uniforme em \(K\) e erro somente nos pontos de treino.
- Mostre que uma combinação finita de ativações polinomiais de grau \(q\) continua sendo um polinômio de grau no máximo \(q\).
- Represente \(f(x)=|x|\) exatamente com duas unidades ReLU.
- Construa uma função com inclinação \(1\) antes de zero e \(3\) depois de zero usando o código anterior.
- Dê um exemplo de afirmação sobre treinamento que não decorre do teorema de aproximação universal.
7.7 Rede neural de múltiplas camadas
Uma rede profunda encadeia várias transformações aprendidas. Cada camada recebe uma representação, produz outra e a entrega à camada seguinte. Como as conexões apontam somente para a frente, o grafo de computação não possui ciclos.
7.7.1 Notação por camadas
Considere uma rede com \(L\) camadas parametrizadas. Definimos \(a^{(0)}=x\in\mathbb R^{n_0}\) e, para \(\ell=1,\ldots,L\),
\[ z^{(\ell)} =W^{(\ell)}a^{(\ell-1)}+b^{(\ell)}, \qquad a^{(\ell)}=g_\ell\!\left(z^{(\ell)}\right). \tag{7.5}\]
Se a camada \(\ell\) possui \(n_\ell\) unidades, então
\[ W^{(\ell)}\in\mathbb R^{n_\ell\times n_{\ell-1}}, \qquad b^{(\ell)},z^{(\ell)},a^{(\ell)}\in\mathbb R^{n_\ell}. \]
A hipótese completa é
\[ h_\theta =h^{(L)}\circ h^{(L-1)}\circ\cdots\circ h^{(1)}, \]
onde \(h^{(\ell)}(a)=g_\ell(W^{(\ell)}a+b^{(\ell)})\). A camada \(h^{(1)}\) aparece à direita porque é aplicada primeiro. Inverter essa ordem é um erro frequente ao escrever composições.

7.7.2 Camadas densas
Em uma camada densa, ou totalmente conectada, cada unidade recebe todas as ativações da camada anterior. Isso explica por que \(W^{(\ell)}\) possui uma entrada para cada par formado por uma unidade da camada atual e uma unidade da camada anterior.
Nem toda rede usa conexões densas. Camadas convolucionais compartilham pesos e exploram estrutura espacial; mecanismos de atenção combinam representações segundo similaridades aprendidas; redes recorrentes compartilham parâmetros ao longo do tempo. A notação de composição ainda é útil, mas os operadores não precisam ser matrizes densas arbitrárias.
7.7.3 Ativações ocultas e ativação de saída
As ativações ocultas \(g_1,\ldots,g_{L-1}\) introduzem não linearidade. Elas não precisam ser iguais em todas as camadas, embora arquiteturas homogêneas sejam comuns. A ativação final \(g_L\) é escolhida conforme o significado desejado para a saída:
- identidade para regressão sem restrição de faixa;
- sigmoide para probabilidade binária ou rótulos independentes;
- softmax para classes mutuamente exclusivas;
- outra transformação específica quando a saída deve obedecer a uma restrição do problema.
Durante o treinamento, normalmente mantemos os valores antes da ativação final, chamados logits, porque perdas combinadas com logits oferecem maior estabilidade numérica.
7.7.4 Contagem de parâmetros
Uma camada densa de largura \(n_{\ell-1}\) para \(n_\ell\) possui
\[ n_\ell n_{\ell-1}+n_\ell =n_\ell(n_{\ell-1}+1) \]
parâmetros: uma matriz de pesos e um viés por unidade. Portanto, a rede inteira possui
\[ P=\sum_{\ell=1}^{L} n_\ell(n_{\ell-1}+1) \tag{7.6}\]
parâmetros treináveis. Por exemplo, a arquitetura \(4\to8\to6\to3\) contém
\[ 8(4+1)+6(8+1)+3(6+1)=115 \]
parâmetros. A contagem inclui os vieses e não conta a camada de entrada como parametrizada.
7.7.5 Cálculo em lote
Quando cada linha representa um exemplo, \(A^{(0)}=X\in\mathbb R^{N\times n_0}\). A propagação usa
\[ Z^{(\ell)} =A^{(\ell-1)}(W^{(\ell)})^{\mathsf T}+b^{(\ell)}, \]
em que o viés é transmitido para as \(N\) linhas por broadcasting. Assim, \(Z^{(\ell)}\) e \(A^{(\ell)}\) têm formato \(N\times n_\ell\).
import numpy as np
def relu(z):
return np.maximum(z, 0.0)
def forward_denso(X, pesos, vieses):
A = np.asarray(X, dtype=float)
cache = []
for W, b in zip(pesos[:-1], vieses[:-1]):
Z = A @ W.T + b
A_novo = relu(Z)
cache.append((A, Z))
A = A_novo
Z_saida = A @ pesos[-1].T + vieses[-1]
cache.append((A, Z_saida))
return Z_saida, cacheO exemplo usa ReLU nas camadas ocultas e deixa a última camada em logits. O cache guarda valores necessários à retropropagação. Em uma biblioteca de autodiferenciação, o grafo computacional cumpre esse papel.
7.7.6 Representações hierárquicas
Cada camada pode reutilizar características construídas pela anterior. Em uma tarefa visual, camadas iniciais podem responder a contrastes locais, enquanto camadas posteriores combinam essas respostas em padrões mais abstratos. Essa interpretação é útil, mas não significa que cada neurônio sempre tenha um significado humano simples.
A profundidade também muda a geometria da otimização. A função pode ser simples no espaço de saída e, ainda assim, possuir muitas parametrizações equivalentes. Gradientes que atravessam várias camadas podem desaparecer ou explodir; inicialização, normalização, conexões residuais e escolha da ativação ajudam a controlar esses efeitos.
Muitos erros de redes neurais são incompatibilidades silenciosas de eixos. Documente se exemplos ocupam linhas ou colunas e teste cada produto matricial. Broadcasting pode produzir uma matriz válida com um significado diferente do pretendido.
7.7.6.1 Exercícios
- Liste os formatos de todos os tensores em uma rede \(5\to10\to4\to2\) para um lote com 32 exemplos.
- Calcule o número de parâmetros dessa rede usando
- Mostre algebricamente que duas camadas sem ativação não linear podem ser substituídas por uma única camada afim.
- Explique por que a camada de entrada não acrescenta parâmetros.
- Modifique o pseudocódigo para aplicar softmax estável na saída apenas durante a inferência.
7.8 Teorema de aproximação universal para redes ReLU limitadas por largura
O teorema anterior permite usar uma única camada oculta, desde que sua largura possa crescer. Há uma possibilidade complementar: manter a largura pequena e permitir que a profundidade cresça. Para redes ReLU, essa troca ainda preserva a capacidade de aproximação universal em certos espaços de funções.
7.8.1 Enunciado em norma \(L^1\)
Seja \(f:\mathbb R^n\to\mathbb R\) uma função integrável no sentido de Lebesgue, isto é,
\[ \lVert f\rVert_1 =\int_{\mathbb R^n}|f(x)|\,dx<\infty. \]
Para todo \(\varepsilon>0\), existe uma rede feedforward totalmente conectada com ativação ReLU, largura máxima não superior a \(n+4\) e profundidade finita tal que a função \(F_\theta\) representada pela rede satisfaz
\[ \lVert f-F_\theta\rVert_1 =\int_{\mathbb R^n}|f(x)-F_\theta(x)|\,dx <\varepsilon. \tag{7.7}\]
A profundidade não é fixada previamente: ela pode aumentar quando a função-alvo se torna mais complexa ou quando \(\varepsilon\) diminui. Portanto, o resultado não afirma que uma arquitetura única de largura \(n+4\) e profundidade fixa aproxima todas as funções.
7.8.2 O significado da largura
A largura de uma rede é o maior número de unidades em uma de suas camadas ocultas. Se as larguras forem \(n_1,\ldots,n_{L-1}\), então
\[ \operatorname{largura}(\mathcal N) =\max_{\ell=1,\ldots,L-1}n_\ell. \]
O limite \(n+4\) depende da dimensão de entrada \(n\), e não do número de exemplos da amostra. Ele é um limite suficiente para o resultado acima, não uma afirmação de que toda função exige exatamente essa largura nem de que esse seja o melhor limite para toda variante do problema.
7.8.3 Comparação com a aproximação por uma camada oculta
Os dois resultados distribuem capacidade de formas diferentes:
| Aspecto | Rede rasa | Rede de largura limitada |
|---|---|---|
| Profundidade | uma camada oculta | pode crescer |
| Largura | pode crescer | no máximo \(n+4\) |
| Funções no enunciado | contínuas em compacto | integráveis em \(\mathbb R^n\) |
| Norma | uniforme \(L^\infty\) | integral \(L^1\) |
| Tipo de garantia | existência | existência |
As normas expressam exigências distintas. O erro uniforme controla o maior desvio:
\[ \lVert f-g\rVert_\infty =\sup_{x\in K}|f(x)-g(x)|. \]
Já o erro \(L^1\) acumula a área — ou o volume — da diferença. Uma aproximação pode ter erro muito grande em uma região de medida pequena e ainda apresentar erro \(L^1\) reduzido. Por isso, os dois teoremas não devem ser comparados apenas pelo número de neurônios.
7.8.4 Intuição da construção
Uma demonstração construtiva pode ser entendida em quatro movimentos.
- Truncar as caudas. Como \(f\in L^1(\mathbb R^n)\), escolhemos um cubo grande fora do qual a integral de \(|f|\) é pequena.
- Aproximar por blocos simples. Dentro do cubo, funções simples, constantes em pequenas regiões, aproximam \(f\) em \(L^1\).
- Construir um bloco por vez. Camadas ReLU profundas formam aproximações das regiões e de seus valores, reutilizando poucas coordenadas auxiliares.
- Acumular os resultados. A rede transporta a entrada e uma soma parcial enquanto acrescenta a contribuição do bloco seguinte.
A largura permanece limitada porque as mesmas unidades funcionam como um pequeno espaço de trabalho reutilizado ao longo da profundidade. A rede troca processamento “em paralelo”, característico de uma camada muito larga, por processamento “sequencial” em muitas camadas.

7.8.5 Por que funções indicadoras podem ser aproximadas?
A ReLU é contínua, portanto uma rede ReLU finita não representa exatamente a descontinuidade de uma função indicadora em todos os pontos. Em norma \(L^1\), isso não é necessário. Podemos substituir a borda abrupta por uma transição estreita e linear por partes. Ao diminuir a espessura da região de transição, sua contribuição ao erro integral também diminui.
Essa observação evidencia a importância da norma. A mesma sequência não converge uniformemente para uma função indicadora em um domínio que contenha sua fronteira, pois o salto permanece descontínuo.
7.8.6 Extensão para várias saídas
Para \(f:\mathbb R^n\to\mathbb R^k\), uma estratégia simples é aproximar cada componente. Contudo, o limite de largura precisa contabilizar as saídas e a arquitetura usada para transportá-las. Não devemos aplicar automaticamente o valor escalar \(n+4\) a qualquer teorema vetorial sem verificar seu enunciado específico.
7.8.7 O que a garantia não resolve
Assim como o teorema de uma camada oculta, Equation 7.7 é um resultado de representação. Ele não fornece:
- uma profundidade pequena;
- uma quantidade pequena de parâmetros;
- um algoritmo para encontrar os pesos;
- estabilidade numérica durante o treinamento;
- garantia de generalização a partir de uma amostra finita;
- boa aproximação ponto a ponto, pois a norma é \(L^1\).
Uma construção existente pode ser profunda demais para a prática. Também pode usar pesos de grande magnitude, o que dificulta a otimização. A escolha de arquitetura continua sendo uma decisão estatística e computacional.
7.8.8 Verificação numérica da norma
Em uma dimensão, se conhecemos uma malha suficientemente fina, podemos aproximar a integral do erro pela regra do trapézio:
import numpy as np
def erro_l1_aproximado(f, g, inicio, fim, pontos=20_001):
x = np.linspace(inicio, fim, pontos)
erro = np.abs(f(x) - g(x))
return np.trapezoid(erro, x)Esse cálculo avalia apenas o intervalo escolhido e produz uma aproximação numérica. Para relacioná-lo à integral em toda a reta, também precisamos limitar ou estimar o erro nas caudas.
7.8.8.1 Exercícios
- Diferencie largura máxima, profundidade e número total de parâmetros.
- Explique por que uma função com erro alto em um intervalo muito estreito pode ter erro \(L^1\) pequeno.
- Mostre que a função indicadora de um intervalo não pode ser aproximada uniformemente por funções contínuas com erro menor que \(1/2\) em um domínio que contenha suas extremidades.
- Para \(n=20\), qual largura suficiente é fornecida pelo enunciado? O resultado determina a profundidade?
- Compare a ideia de computação paralela de uma rede larga com a reutilização sequencial de unidades em uma rede profunda.
7.9 Largura versus profundidade das redes neurais
Os teoremas anteriores mostram que tanto a largura quanto a profundidade podem fornecer capacidade de aproximação. Eles não dizem, porém, que duas arquiteturas com o mesmo número de parâmetros sejam igualmente eficientes para representar uma função particular.
Largura oferece muitas unidades que operam em paralelo sobre a mesma representação. Profundidade permite compor transformações e reutilizar resultados intermediários. Essa diferença é análoga à comparação entre uma expressão expandida e um programa que cria valores auxiliares e os reaproveita.
7.9.1 Composição e regiões lineares
Redes ReLU representam funções lineares por partes. Em uma dimensão, uma camada com várias ReLUs pode introduzir vários pontos de quebra. Ao compor camadas, os pontos de quebra de uma transformação podem ser replicados nas regiões criadas anteriormente, fazendo o número de trechos crescer rapidamente.
Considere a função triangular em \([0,1]\):
\[ T(x)=2\operatorname{ReLU}(x) -4\operatorname{ReLU}\!\left(x-\frac12\right) +2\operatorname{ReLU}(x-1). \]
Ela sobe de \(0\) a \(1\) e depois desce a \(0\). A composição \(T\circ T\) produz mais oscilações; repetir a composição aumenta seu número sem precisar reescrever cada trecho independentemente.

Resultados de separação de profundidade formalizam essa intuição: há famílias de funções que redes profundas representam com quantidade moderada de unidades, mas cuja aproximação por redes com profundidade limitada exige largura muito maior, em alguns enunciados de modo exponencial. Toda afirmação desse tipo depende das hipóteses — ativação, norma, erro permitido, domínio e limites impostos aos pesos — e não deve ser convertida em uma regra universal sobre qualquer conjunto de dados.
7.9.2 Quando a largura ajuda
Camadas mais largas podem:
- aprender várias características no mesmo nível de abstração;
- reduzir gargalos que descartariam informação cedo demais;
- aumentar o paralelismo disponível em aceleradores;
- oferecer caminhos curtos entre entrada e saída.
Largura excessiva também aumenta memória, produtos matriciais e risco de redundância. Mais parâmetros não substituem dados, regularização ou uma função objetivo apropriada.
7.9.3 Quando a profundidade ajuda
Profundidade é especialmente natural quando a tarefa possui estrutura composicional: pixels formam padrões locais, padrões formam partes e partes formam objetos; em linguagem, símbolos formam expressões e expressões participam de contextos maiores.
Entretanto, redes profundas possuem caminhos de derivação longos. Isso pode causar gradientes que desaparecem ou explodem, elevar a latência sequencial e tornar a otimização mais sensível. Conexões residuais, normalização e inicialização adequada foram desenvolvidas, em parte, para tornar redes profundas treináveis.
7.9.4 Gargalos e conexões residuais
Uma camada muito estreita pode funcionar como gargalo. Isso é útil se queremos compressão, mas prejudicial quando elimina informação necessária às camadas seguintes. Uma conexão residual calcula
\[ a^{(\ell+1)}=a^{(\ell)}+F_\ell(a^{(\ell)}), \]
desde que os formatos sejam compatíveis. A parcela identidade oferece um caminho direto para informação e gradientes, permitindo que o bloco aprenda uma correção em vez de reconstruir toda a representação.
7.9.5 Escolha orientada por validação
Não existe uma proporção universal entre largura e profundidade. Uma comparação justa deve controlar, quando possível:
- número total de parâmetros ou custo computacional;
- procedimento de treinamento e orçamento de épocas;
- regularização e transformação dos dados;
- métrica em validação, latência e uso de memória;
- variabilidade entre inicializações aleatórias.
Uma arquitetura menor que atinge a métrica necessária com treinamento estável costuma ser preferível. Teoremas de expressividade ajudam a entender possibilidades e limitações, mas a escolha final combina teoria com evidência experimental.
def contar_parametros_densos(larguras):
return sum(
saida * (entrada + 1)
for entrada, saida in zip(larguras[:-1], larguras[1:])
)
print(contar_parametros_densos([20, 64, 10]))
print(contar_parametros_densos([20, 24, 24, 10]))Comparar apenas a quantidade de camadas seria enganoso: o código permite verificar também o orçamento de parâmetros das arquiteturas candidatas.
7.9.5.1 Exercícios
- Esboce \(T(x)\) e \(T(T(x))\) no intervalo \([0,1]\).
- Explique por que composição pode ser mais econômica que enumerar diretamente todos os trechos de uma função.
- Calcule os parâmetros das duas arquiteturas do exemplo em Python.
- Dê um exemplo de tarefa com estrutura naturalmente composicional.
- Por que um resultado de separação teórica não garante que uma rede mais profunda terá melhor acurácia em qualquer base real?
7.10 Descida do Gradiente
Treinar uma rede significa escolher seus parâmetros \(\theta=\{W^{(\ell)},b^{(\ell)}\}_{\ell=1}^L\) para reduzir uma função objetivo. Como redes profundas compõem muitas operações não lineares, essa função raramente possui uma solução fechada. Usamos então métodos iterativos baseados em derivadas.
7.10.1 Função objetivo
Para uma amostra \(D=\{(x_i,y_i)\}_{i=1}^N\), definimos
\[ E_D(\theta) =\frac1N\sum_{i=1}^N \ell(h_\theta(x_i),y_i)+\lambda R(\theta), \tag{7.8}\]
onde \(\ell\) é a perda por exemplo, \(R\) é um termo opcional de regularização e \(\lambda\geq0\) controla sua força. Alguns exemplos são:
- erro quadrático em regressão;
- entropia cruzada binária para dois resultados;
- entropia cruzada categórica para classes exclusivas.
A escolha deve combinar o significado da saída, o modelo probabilístico e a estabilidade numérica. A acurácia, apesar de útil para avaliação, é descontínua e não costuma servir diretamente como objetivo de gradiente.
7.10.2 Direção de maior descida
Para uma pequena perturbação \(\Delta\theta\), a aproximação de primeira ordem é
\[ E_D(\theta+\Delta\theta) \approx E_D(\theta) +\nabla E_D(\theta)^{\mathsf T}\Delta\theta. \]
Entre direções unitárias, o gradiente aponta para o maior crescimento local e seu oposto aponta para o maior decréscimo. A descida do gradiente atualiza
\[ \theta_{t+1} =\theta_t-\eta_t\nabla E_D(\theta_t), \tag{7.9}\]
com taxa de aprendizado \(\eta_t>0\). O nome “máximo declive” descreve a direção negativa do gradiente, mas “descida do gradiente” é a terminologia mais comum.

Não normalizamos o gradiente na atualização padrão. Sua magnitude contém informação sobre a inclinação local. Métodos com gradiente normalizado existem, mas constituem outro algoritmo e mudam a interpretação de \(\eta_t\).
7.10.3 Lote completo, estocástico e mini-lote
Calcular Equation 7.8 sobre todos os exemplos fornece o gradiente de lote completo. Para grandes bases, dividimos os dados em mini-lotes \(B_t\) e usamos
\[ g_t =\frac1{|B_t|}\sum_{i\in B_t} \nabla_\theta\ell(h_\theta(x_i),y_i) +\lambda\nabla_\theta R(\theta). \]
A atualização torna-se \(\theta_{t+1}=\theta_t-\eta_tg_t\). Um lote com um exemplo caracteriza a descida estocástica; lotes intermediários aproveitam vetorização e introduzem ruído que pode ajudar a exploração.
Uma época é uma passagem pelos exemplos do treino. Uma iteração é uma atualização. Com \(N\) exemplos e lotes de tamanho \(B\), uma época contém aproximadamente \(\lceil N/B\rceil\) iterações.
7.10.4 Taxa de aprendizado
Se \(\eta\) for muito pequena, o treinamento avança lentamente. Se for muito grande, a perda pode oscilar, divergir ou produzir valores não finitos. Não há um valor como \(0{,}1\) que seja adequado para todas as redes.
Podemos usar taxa constante, decaimento programado ou adaptação baseada no histórico dos gradientes. Momentum acumula uma média das direções; Adam adapta escalas por parâmetro. Esses métodos facilitam o treinamento, mas ainda exigem monitoramento e não garantem o mínimo global.
7.10.5 Inicialização e quebra de simetria
Inicializar todos os pesos ocultos em zero faz neurônios da mesma camada receberem gradientes iguais. Eles permanecem idênticos e desperdiçam a largura disponível. Usamos valores aleatórios com variância relacionada ao número de entradas, como inicializações de Xavier ou He, dependendo da ativação.
Vieses podem frequentemente começar em zero porque pesos aleatórios já quebram a simetria. A semente aleatória deve ser registrada para tornar experimentos reproduzíveis.
7.10.6 Critérios de parada
Gradiente exatamente zero não é um critério realista e pode indicar mínimo, máximo, ponto de sela ou saturação. Em prática, combinamos:
- limite máximo de épocas ou tempo;
- perda e métricas em treino e validação;
- ausência de melhora na validação por certo número de épocas;
- norma do gradiente ou variação dos parâmetros;
- verificação de valores NaN ou infinitos.
A parada antecipada guarda os parâmetros com melhor validação, não necessariamente os da última época. O conjunto de teste permanece reservado até o fim.
7.10.7 Laço mínimo de otimização
def treinar(modelo, lotes, otimizador, epocas):
for epoca in range(epocas):
modelo.modo_treino()
for X, y in lotes:
otimizador.zerar_gradientes()
logits = modelo(X)
perda = funcao_perda(logits, y)
perda.retropropagar()
otimizador.atualizar()
avaliar_na_validacao(modelo)Zerar gradientes é explícito porque muitas bibliotecas os acumulam por padrão. A retropropagação calcula derivadas; o otimizador decide como transformá-las em atualizações.
Em mini-lotes, a perda do lote seguinte pode aumentar mesmo quando a tendência global melhora. Além disso, a norma do gradiente pode crescer temporariamente. Curvas suavizadas e métricas de validação são mais informativas que uma única iteração.
7.10.7.1 Exercícios
- Derive Equation 7.9 a partir da aproximação de primeira ordem e da escolha \(\Delta\theta=-\eta\nabla E_D\).
- Quantas atualizações há por época para \(N=10\,000\) e \(B=128\)?
- Explique por que pesos ocultos todos iguais preservam simetria.
- Descreva sinais observáveis de uma taxa de aprendizado excessiva.
- Diferencie perda de treino, perda de validação e métrica de teste.
7.11 Vetorização
Vetorização consiste em expressar várias operações escalares como uma operação sobre vetores ou matrizes. Além de tornar a notação compacta, ela permite que bibliotecas usem rotinas altamente otimizadas e executem muitas multiplicações em paralelo em CPU, GPU ou outros aceleradores.
7.11.1 Convenção de eixos
Adotaremos uma regra única:
- cada linha contém um exemplo;
- cada coluna contém um atributo ou uma unidade;
- a matriz de pesos armazena um neurônio por linha.
Assim, para uma camada que recebe \(d\) valores e produz \(m\) ativações,
\[ W\in\mathbb R^{m\times d}, \qquad b\in\mathbb R^m. \]
Para um único vetor-coluna \(x\in\mathbb R^d\), usamos
\[ z=Wx+b\in\mathbb R^m. \]
Já no código em lote, os exemplos ocupam linhas, e a transposta de \(W\) aparece à direita.
7.11.2 Uma camada sobre um lote
Se \(X\in\mathbb R^{B\times d}\) representa um mini-lote com \(B\) exemplos, então
\[ Z=XW^{\mathsf T}+\mathbf1_Bb^{\mathsf T} \in\mathbb R^{B\times m}, \tag{7.10}\]
e
\[ A=g(Z)\in\mathbb R^{B\times m}. \]
A entrada \(Z_{ij}\) é a pré-ativação da unidade \(j\) para o exemplo \(i\). A ativação é aplicada elemento a elemento, salvo funções vetoriais como softmax, que operam ao longo do eixo das classes.

7.11.3 Broadcasting do viés
Não precisamos construir materialmente a matriz \(\mathbf1_Bb^{\mathsf T}\). Ao somar um vetor de formato (m,) a uma matriz (B, m), NumPy e bibliotecas semelhantes repetem logicamente o vetor em cada linha. Esse mecanismo é chamado broadcasting.
Broadcasting economiza memória, mas pode esconder erros. Um viés com formato (B, 1) também pode ser somado a Z, porém acrescentaria um valor por exemplo, não um valor por unidade. Os formatos seriam compatíveis e o significado estaria errado.
7.11.4 Várias camadas
Para uma arquitetura \(n_0\to n_1\to\cdots\to n_L\) e um lote com \(B\) exemplos,
\[ A^{(0)}=X\in\mathbb R^{B\times n_0}, \]
\[ Z^{(\ell)} =A^{(\ell-1)}(W^{(\ell)})^{\mathsf T}+b^{(\ell)}, \qquad A^{(\ell)}=g_\ell(Z^{(\ell)}). \]
Em cada camada, somente a segunda dimensão muda: de \(n_{\ell-1}\) para \(n_\ell\). A dimensão \(B\) do lote é preservada.
import numpy as np
def camada_densa(A, W, b, ativacao):
A = np.asarray(A)
W = np.asarray(W)
b = np.asarray(b)
if A.ndim != 2 or W.ndim != 2 or b.ndim != 1:
raise ValueError("A e W devem ser matrizes; b deve ser vetor")
if A.shape[1] != W.shape[1] or W.shape[0] != b.shape[0]:
raise ValueError("dimensões incompatíveis na camada densa")
Z = A @ W.T + b
return ativacao(Z), ZAs verificações tornam a convenção explícita. Em código de produção, testes de unidade com lotes pequenos ajudam a confirmar valores, não apenas formatos.
7.11.5 Por que é mais rápido?
Um laço Python que calcula cada neurônio e cada exemplo separadamente tem grande custo de interpretação. A multiplicação matricial delega o trabalho a implementações compiladas, usa instruções vetoriais, memória em blocos e paralelismo. A quantidade matemática de multiplicações não desaparece, mas a execução utiliza melhor o hardware.
Vetorização também possui limites. Um lote muito grande pode exceder a memória, e armazenar todas as ativações é caro durante o treinamento. Mini-lotes equilibram paralelismo, memória e frequência de atualização dos parâmetros.
7.11.6 Vetorização da perda
Para regressão com \(k\) saídas, se \(\widehat Y,Y\in\mathbb R^{B\times k}\), o erro quadrático médio pode ser escrito como
\[ E_B=\frac1{Bk}\lVert\widehat Y-Y\rVert_F^2, \]
onde \(\lVert\cdot\rVert_F\) é a norma de Frobenius. É preciso declarar se a redução usa soma ou média e sobre quais eixos; isso altera a escala do gradiente e, consequentemente, a taxa de aprendizado adequada.
7.11.6.1 Exercícios
- Determine o formato de \(Z\) para \(B=64\), \(d=100\) e \(m=32\).
- Escreva Equation 7.10 usando exemplos como colunas e compare onde aparece a transposta.
- Explique o erro semântico de somar um vetor
(B, 1)aZ. - Por que lotes maiores não reduzem a quantidade matemática de multiplicações, embora possam reduzir o tempo de execução?
- Compare o gradiente de uma perda somada com o da mesma perda média.
7.12 Autodiferenciação reversa pela regra da cadeia
Para atualizar milhões de parâmetros, precisamos calcular muitas derivadas de uma única saída escalar: a perda. O modo reverso da autodiferenciação é adequado a essa estrutura. Ele executa o grafo para frente, guarda valores intermediários e percorre as operações na ordem inversa, aplicando a regra da cadeia.
Autodiferenciação não é diferenciação simbólica nem aproximação por diferenças finitas. Ela aplica regras exatas de derivação às operações elementares realizadas pelo programa, sujeita apenas ao arredondamento numérico.
7.12.1 Um grafo computacional simples
Considere
\[ z=wx+b,\qquad a=g(z),\qquad E=\ell(a,y). \]
A passagem para frente calcula \(z\), \(a\) e \(E\). No sentido inverso,
\[ \frac{\partial E}{\partial z} =\frac{\partial E}{\partial a} \frac{\partial a}{\partial z}, \]
e depois
\[ \frac{\partial E}{\partial w} =\frac{\partial E}{\partial z} \frac{\partial z}{\partial w}, \qquad \frac{\partial E}{\partial b} =\frac{\partial E}{\partial z} \frac{\partial z}{\partial b}. \]
Como \(\partial z/\partial w=x\) e \(\partial z/\partial b=1\), obtemos
\[ \frac{\partial E}{\partial w}=\delta x, \qquad \frac{\partial E}{\partial b}=\delta, \qquad \delta:=\frac{\partial E}{\partial z}. \]

7.12.2 Produtos vetor–Jacobiano
Uma camada pode produzir um vetor. Se \(u=f(v)\) e a perda escalar é \(E(u)\), a regra da cadeia pode ser escrita como
\[ \nabla_v E =J_f(v)^{\mathsf T}\nabla_u E, \]
onde \(J_f\) é o Jacobiano de \(f\). O modo reverso não precisa armazenar todo esse Jacobiano: calcula diretamente o produto \(J_f(v)^{\mathsf T}\nabla_u E\), chamado produto vetor–Jacobiano.
Isso é crucial em redes. Formar explicitamente cada Jacobiano seria caro, enquanto propagar um vetor de derivadas possui custo da mesma ordem da passagem para frente.
7.12.3 Derivação para uma camada densa
Para um exemplo, a camada \(\ell\) calcula
\[ z^{(\ell)} =W^{(\ell)}a^{(\ell-1)}+b^{(\ell)}, \qquad a^{(\ell)}=g_\ell(z^{(\ell)}). \]
Defina o sinal retropropagado
\[ \delta^{(\ell)} :=\frac{\partial E}{\partial z^{(\ell)}}. \tag{7.11}\]
Usando produtos externos, os gradientes dos parâmetros são
\[ \frac{\partial E}{\partial W^{(\ell)}} =\delta^{(\ell)}(a^{(\ell-1)})^{\mathsf T}, \tag{7.12}\]
\[ \frac{\partial E}{\partial b^{(\ell)}} =\delta^{(\ell)}. \tag{7.13}\]
Os formatos confirmam a expressão:
\[ (n_\ell\times1)(1\times n_{\ell-1}) =n_\ell\times n_{\ell-1}. \]
7.12.4 Última camada
A forma de \(\delta^{(L)}\) depende da ativação e da perda. Em geral,
\[ \delta^{(L)} =\nabla_{a^{(L)}}E \odot g_L'(z^{(L)}), \]
onde \(\odot\) denota produto elemento a elemento.
Para erro quadrático \(E=\frac12\lVert a^{(L)}-y\rVert^2\),
\[ \delta^{(L)} =(a^{(L)}-y)\odot g_L'(z^{(L)}). \]
Para softmax combinada com entropia cruzada categórica, a derivação simplifica para
\[ \delta^{(L)}=p-y, \]
em que \(p=\operatorname{softmax}(z^{(L)})\) e \(y\) é o vetor alvo. Essa fórmula pressupõe a combinação específica de ativação e perda; não deve ser reutilizada indiscriminadamente.
7.12.5 Camadas ocultas
A pré-ativação da camada seguinte depende de \(a^{(\ell)}\):
\[ z^{(\ell+1)} =W^{(\ell+1)}a^{(\ell)}+b^{(\ell+1)}. \]
Aplicando a regra da cadeia,
\[ \delta^{(\ell)} =\left((W^{(\ell+1)})^{\mathsf T} \delta^{(\ell+1)}\right) \odot g_\ell'(z^{(\ell)}). \tag{7.14}\]
A matriz transposta redistribui para cada unidade da camada atual as contribuições de todas as unidades da camada seguinte. Em seguida, o produto por \(g_\ell'\) atravessa a ativação local.
A recorrência começa em \(\delta^{(L)}\) e segue até a primeira camada. Depois de obter cada \(\delta^{(\ell)}\), usamos Equation 7.12 e Equation 7.13.
7.12.6 Forma vetorizada para mini-lotes
Com exemplos nas linhas, sejam \(A^{(\ell-1)}\in\mathbb R^{B\times n_{\ell-1}}\) e \(\Delta^{(\ell)}\in\mathbb R^{B\times n_\ell}\). Se a perda é a média do lote,
\[ \frac{\partial E}{\partial W^{(\ell)}} =\frac1B(\Delta^{(\ell)})^{\mathsf T}A^{(\ell-1)}, \]
\[ \frac{\partial E}{\partial b^{(\ell)}} =\frac1B\sum_{i=1}^B\Delta^{(\ell)}_{i,:}, \]
e a propagação para a camada anterior é
\[ \Delta^{(\ell-1)} =\left(\Delta^{(\ell)}W^{(\ell)}\right) \odot g_{\ell-1}'(Z^{(\ell-1)}). \]
O eixo do somatório do viés é o dos exemplos. Somar sobre as unidades produziria um vetor com formato e significado errados.
7.12.7 Ramificações no grafo
Quando um valor alimenta dois caminhos, a perda depende dele por ambos. A derivada total é a soma das contribuições. Se \(u\) influencia \(E\) através de \(r_1(u)\) e \(r_2(u)\), então
\[ \frac{\partial E}{\partial u} =\left.\frac{\partial E}{\partial u}\right|_{r_1} +\left.\frac{\partial E}{\partial u}\right|_{r_2}. \]
Isso explica por que bibliotecas acumulam gradientes e por que conexões residuais somam caminhos na passagem reversa.
7.12.8 Verificação por diferenças finitas
Diferenças centrais oferecem uma checagem de implementação:
\[ \frac{\partial E}{\partial\theta_j} \approx \frac{E(\theta_j+h)-E(\theta_j-h)}{2h}. \]
def derivada_numerica(perda, theta, indice, h=1e-5):
original = theta[indice]
theta[indice] = original + h
mais = perda()
theta[indice] = original - h
menos = perda()
theta[indice] = original
return (mais - menos) / (2 * h)Esse método requer duas avaliações por parâmetro, sofre com escolha de \(h\) e arredondamento, e não é adequado para treinamento. Ele serve como teste em redes pequenas, usando precisão dupla e evitando pontos não diferenciáveis da ReLU.
A retropropagação calcula derivadas. Ela não altera parâmetros por si só. O otimizador usa essas derivadas posteriormente. Misturar os dois papéis dificulta testar e reutilizar a implementação.
7.12.8.1 Exercícios
- Derive os gradientes de \(z=wx+b\) sem omitir as etapas da regra da cadeia.
- Verifique os formatos em Equation 7.14 para uma camada \(n_{\ell-1}\to n_\ell\to n_{\ell+1}\).
- Explique por que gradientes de dois caminhos devem ser somados.
- Derive \(\delta^{(L)}\) para saída identidade e erro quadrático.
- Compare custo e finalidade da autodiferenciação reversa e das diferenças finitas.
7.13 Algoritmo Propagação para Trás
Retropropagação, ou backpropagation, é a aplicação especializada do modo reverso da autodiferenciação a uma rede neural. Seu resultado são os gradientes da perda em relação a todos os parâmetros. Descida do gradiente, Adam ou outro otimizador usa esses gradientes em uma etapa posterior.
7.13.1 Visão geral
Para um mini-lote, uma iteração de treinamento possui quatro fases:
- Propagação direta: calcular \(Z^{(\ell)}\) e \(A^{(\ell)}\) da primeira à última camada e guardar os valores necessários.
- Perda: calcular \(E\) a partir da saída e dos alvos.
- Propagação reversa: iniciar \(\Delta^{(L)}\) na saída e percorrer as camadas de \(L\) até \(1\), calculando gradientes.
- Atualização: aplicar o otimizador aos parâmetros.
Para descida do gradiente simples, a atualização correta é
\[ W^{(\ell)}_{t+1} =W^{(\ell)}_t -\eta_t\frac{\partial E}{\partial W^{(\ell)}_t}, \]
\[ b^{(\ell)}_{t+1} =b^{(\ell)}_t -\eta_t\frac{\partial E}{\partial b^{(\ell)}_t}. \]
O parâmetro antigo, no instante \(t\), aparece no lado direito. Usar o índice \(t+1\) nos dois lados tornaria a regra circular.
7.13.2 Algoritmo vetorizado
Com exemplos nas linhas e uma perda média, a propagação reversa executa, para \(\ell=L,L-1,\ldots,1\),
\[ dW^{(\ell)} =\frac1B(\Delta^{(\ell)})^{\mathsf T}A^{(\ell-1)}, \]
\[ db^{(\ell)} =\frac1B\sum_{i=1}^{B}\Delta^{(\ell)}_{i,:}, \]
e, se \(\ell>1\),
\[ \Delta^{(\ell-1)} =\left(\Delta^{(\ell)}W^{(\ell)}\right) \odot g'_{\ell-1}(Z^{(\ell-1)}). \]
O fator \(1/B\) deve aparecer uma única vez. Podemos incorporá-lo no delta inicial ou nos gradientes dos parâmetros, mas duplicá-lo reduz o gradiente por \(B^2\) e omiti-lo faz sua escala crescer com o lote.
7.13.3 Exemplo completo com duas camadas
Considere ReLU na camada oculta, saída identidade e
\[ E=\frac1{2B}\sum_{i=1}^{B} \lVert\widehat y_i-y_i\rVert^2. \]
import numpy as np
def forward(X, W1, b1, W2, b2):
Z1 = X @ W1.T + b1
A1 = np.maximum(Z1, 0.0)
Y_hat = A1 @ W2.T + b2
cache = (X, Z1, A1)
return Y_hat, cache
def backward(Y_hat, Y, cache, W2):
X, Z1, A1 = cache
B = X.shape[0]
# Delta da saída; a média 1/B entra aqui.
D2 = (Y_hat - Y) / B
dW2 = D2.T @ A1
db2 = D2.sum(axis=0)
D1 = (D2 @ W2) * (Z1 > 0)
dW1 = D1.T @ X
db1 = D1.sum(axis=0)
return dW1, db1, dW2, db2
def atualizar(parametros, gradientes, taxa):
for nome, gradiente in zip(parametros, gradientes):
nome -= taxa * gradienteSe Y_hat e Y tiverem formato (B, k), então D2 também terá (B, k). A função pressupõe que todas as matrizes são de ponto flutuante e que a atualização no lugar é desejada.
7.13.4 Cache e memória
Para calcular \(g'_{\ell}(Z^{(\ell)})\) e os produtos com \(A^{(\ell-1)}\), guardamos ativações da passagem direta. Em redes muito profundas, esse armazenamento domina a memória de treinamento.
Uma alternativa é o checkpointing: guardar apenas algumas ativações e recalcular outras durante a passagem reversa. Isso troca tempo de computação por memória. Na inferência, não precisamos do grafo reverso e podemos liberar intermediários mais cedo.
7.13.5 Ordem segura da atualização
Todos os gradientes devem ser calculados com os mesmos valores dos parâmetros da passagem direta. Atualizar \(W^{(L)}\) antes de calcular \(\Delta^{(L-1)}\) faria a recorrência usar um peso novo, inconsistente com o grafo que produziu a perda. Primeiro calculamos todos os gradientes; somente depois o otimizador altera os parâmetros.
7.13.6 Testes recomendados
Uma implementação própria deve ser verificada progressivamente:
- testar cada camada direta com valores pequenos conhecidos;
- conferir formatos de ativações e gradientes;
- comparar gradientes a diferenças finitas em uma rede minúscula;
- confirmar que um passo suficientemente pequeno reduz a perda do mesmo lote;
- tentar sobreajustar deliberadamente um conjunto com poucos exemplos;
- verificar valores não finitos e normas de gradiente durante o treino.
Sobreajustar um lote pequeno não comprova generalização, mas é um teste útil do encadeamento entre modelo, perda, retropropagação e atualização.
Calcular cada derivada parcial do zero repetiria grande parte do grafo. A eficiência do algoritmo vem de armazenar derivadas intermediárias e reutilizá-las para todos os parâmetros anteriores.
7.13.6.1 Exercícios
- Verifique os formatos de
D1,dW1,db1,D2,dW2edb2. - Derive o delta da saída usado no exemplo a partir do erro quadrático.
- Explique por que os pesos só devem ser atualizados após todos os gradientes terem sido calculados.
- O que muda nas fórmulas se a perda somar os exemplos em vez de usar a média?
- Descreva como o checkpointing troca memória por computação.
7.13.7 Síntese do capítulo
Redes neurais compõem transformações afins e ativações não lineares para aprender representações. Largura e profundidade oferecem formas complementares de expressividade, mas não garantem otimização ou generalização. O treinamento combina operações vetorizadas, uma perda diferenciável, autodiferenciação reversa e um otimizador. Acompanhar formatos, escalas e separação entre essas etapas torna a implementação mais correta e mais fácil de depurar.
7.14 Exercícios de múltipla escolha
Em cada questão, assinale a única alternativa correta. Use os formatos das matrizes para conferir as respostas que envolvem vetorização.
Se todas as ativações de uma rede densa forem funções lineares, a composição de várias camadas será equivalente a:
- uma única transformação afim.
- uma árvore de decisão.
- um núcleo RBF.
- uma função necessariamente periódica.
- uma única transformação afim.
A função ReLU é definida por:
- \(\max(0,z)\).
- \(1/(1+e^{-z})\).
- \(\exp(z)/\sum_j\exp(z_j)\).
- \(z^2\).
- \(\max(0,z)\).
Para classificação multiclasse com classes mutuamente exclusivas, uma ativação de saída usual é:
- ReLU aplicada a um único número.
- softmax.
- valor absoluto.
- identidade seguida de arredondamento obrigatório.
- ReLU aplicada a um único número.
Uma camada densa com 5 entradas e 3 neurônios possui quantos parâmetros, incluindo um viés por neurônio?
O teorema de aproximação universal afirma, sob suas condições, que uma rede:
- pode aproximar funções de uma classe-alvo com precisão arbitrária.
- sempre encontra automaticamente os melhores pesos.
- generaliza perfeitamente com um exemplo.
- requer exatamente duas unidades ocultas.
- pode aproximar funções de uma classe-alvo com precisão arbitrária.
Em uma rede ReLU, aumentar a profundidade pode ser especialmente útil para:
- representar composições hierárquicas.
- eliminar todos os hiperparâmetros.
- dispensar ativações não lineares.
- garantir convexidade da perda nos pesos.
- representar composições hierárquicas.
Na descida do gradiente, a atualização padrão é:
- \(\theta\leftarrow\theta+\eta\nabla J(\theta)\).
- \(\theta\leftarrow\theta-\eta\nabla J(\theta)\).
- \(\theta\leftarrow\nabla J(\theta)/0\).
- \(\theta\leftarrow\theta^2\).
- \(\theta\leftarrow\theta+\eta\nabla J(\theta)\).
Para um lote \(X\in\mathbb{R}^{N\times d}\) e pesos \(W\in\mathbb{R}^{d\times m}\), a saída pré-ativação \(Z=XW+b\) possui formato:
- \(d\times d\).
- \(m\times N\) obrigatoriamente.
- \(N\times m\).
- \(N\times d\) em todos os casos.
- \(d\times d\).
Na autodiferenciação reversa, produtos vetor–Jacobiano permitem:
- propagar derivadas da saída para os nós anteriores do grafo.
- apagar o grafo antes de calcular a perda.
- substituir todos os dados por escalares.
- evitar a regra da cadeia.
- propagar derivadas da saída para os nós anteriores do grafo.
Durante a retropropagação, os pesos devem ser atualizados:
- assim que cada derivada intermediária surgir, mesmo que pesos antigos ainda sejam necessários.
- somente depois de calcular os gradientes necessários para a iteração.
- antes da propagação para frente.
- apenas quando a perda for exatamente zero.
- assim que cada derivada intermediária surgir, mesmo que pesos antigos ainda sejam necessários.
- a. A composição de transformações afins continua sendo afim; sem não linearidade, profundidade não amplia essa classe funcional.
- a. A ReLU preserva entradas positivas e zera entradas negativas.
- b. A softmax produz probabilidades não negativas cuja soma é um.
- c. Há \(5\times3=15\) pesos e 3 vieses, totalizando 18 parâmetros.
- a. O resultado trata de existência e aproximação, não garante treinamento, eficiência ou generalização.
- a. Camadas sucessivas podem reutilizar e compor características de níveis anteriores.
- b. O sinal negativo aponta localmente na direção de redução da função objetivo.
- c. O produto \((N\times d)(d\times m)\) resulta em \(N\times m\), e o viés é difundido por linha.
- a. O modo reverso acumula sensibilidades por meio da regra da cadeia sem formar Jacobianas completas desnecessárias.
- b. Atualizar antes do fim pode misturar valores novos e antigos e produzir gradientes inconsistentes.